﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using EmbeddedLinkUpdater.Code.Events;
using System.Data;
using System.Threading.Tasks;

namespace EmbeddedLinkUpdater.Code
{
    public class DocumentManager
    {
        public event DocumentUpdateDelegate DocumentUpdateEvent;
        public delegate void DocumentUpdateDelegate(object sender, DocumentUpdateDelegateArgs args);
        public int DocumentsProcessedCount { get; set; }

        public void OnDocumentUpdateEvent(DocumentUpdateDelegateArgs args)
        {
            DocumentUpdateDelegate handler = DocumentUpdateEvent;
            if (handler != null) handler(this, args);
        }

        public event DocumentLinksDelegate DocumentLinksEvent;
        public delegate void DocumentLinksDelegate(object sender, DocumentLinksDelegateArgs args);

        public void OnDocumentLinksEvent(DocumentLinksDelegateArgs args)
        {
            DocumentLinksDelegate handler = DocumentLinksEvent;
            if (handler != null) handler(this, args);
        }

        public List<string> SuccessfullDocumentList { get; set; }
        public List<string> FailedDocumentList { get; set; }

        public DocumentManager()
        {
            SuccessfullDocumentList = new List<string>();
            FailedDocumentList = new List<string>();
        }

        public void UpdateLinks(string directory, string documentTypes, DataSet linksToReplace)
        {
            var directories = Directory.GetDirectories(directory, "*.*", SearchOption.AllDirectories);

            Parallel.ForEach(directories, currentDirectory =>
            {
                var files = GetFiles(currentDirectory, DocumentFactory.GetSupportedDocumentTypes(documentTypes), SearchOption.TopDirectoryOnly);

                Parallel.ForEach(files, currentFile =>
                {
                    DocumentsProcessedCount += 1;

                    var document = DocumentFactory.GetDocumentType(currentFile);

                    try
                    {
                        if (document == null)
                            throw new ApplicationException("Failed to update. Document type not found.");

                        document.UpdateDocumentLinks(linksToReplace);

                        OnDocumentUpdateEvent(new DocumentUpdateDelegateArgs(currentFile, true, "Successfully updated."));
                    }
                    catch (ApplicationException ex)
                    {
                        OnDocumentUpdateEvent(new DocumentUpdateDelegateArgs(currentFile, false, ex.Message));
                    }
                });
            });
        }

        public void AnalyzeLinks(string directory, string documentTypes)
        {
            var directories = Directory.GetDirectories(directory, "*.*", SearchOption.AllDirectories);

            Parallel.ForEach(directories, currentDirectory =>
            {
                var files = GetFiles(currentDirectory, DocumentFactory.GetSupportedDocumentTypes(documentTypes), SearchOption.TopDirectoryOnly);

                Parallel.ForEach(files, currentFile =>
                {
                    DocumentsProcessedCount += 1;

                    var document = DocumentFactory.GetDocumentType(currentFile);

                    try
                    {
                        if (document == null)
                            throw new ApplicationException("Failed to update. Document type not found.");

                        var links = document.AnalyzeLinks();

                        OnDocumentLinksEvent(new DocumentLinksDelegateArgs(currentFile, links, true, "Successfully analyzed."));
                    }
                    catch (ApplicationException ex)
                    {
                        OnDocumentLinksEvent(new DocumentLinksDelegateArgs(currentFile, null, false, ex.Message));
                    }
                });
            });
        }

        public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
        {
            var searchPatterns = searchPattern.Split('|').Where(s => s != String.Empty);
            var files = new List<string>();
            foreach (var sp in searchPatterns)
                files.AddRange(Directory.GetFiles(path, "*.*", searchOption).Where(s => s.EndsWith(sp)));
            files.Sort();
            return files.ToArray();
        }

    }
}
